package com.iwdnb.gkgz.application.strategy;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.iwdnb.bmnf.common.utils.BooleanUtils;
import com.iwdnb.gkgz.application.model.request.AddStrategyTradeStockDataRequest;
import com.iwdnb.gkgz.common.enums.StockImageProfitTypeEnums;
import com.iwdnb.gkgz.common.model.dto.TldMatchInfo.TldMatchItem;
import com.iwdnb.gkgz.common.utils.TulongdaoUtils;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StrategyTradeDTO;
import com.iwdnb.gkgz.common.quota.Macd;
import com.iwdnb.gkgz.common.quota.Macd.MacdData;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.DateUtils;
import com.iwdnb.gkgz.common.utils.StockRangeUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;
import static com.iwdnb.gkgz.common.utils.StockUtils.getBeforeLimitUpCount;
import static com.iwdnb.gkgz.common.utils.StockUtils.isLimitUp;
import static com.iwdnb.gkgz.common.utils.StockUtils.isOneLineStock;

@Service
@Slf4j
public class TulongdaoStockBackValidateService extends CommmonStockBackValidateService {

    public static final String STATEGY_CODE = "tulongdao";

    private BigDecimal fuFive = new BigDecimal(-5);
    private BigDecimal sixPointFive = new BigDecimal(6.5);

    @Override
    public List<StrategyTradeDTO> queryBackValidateDateList(String code, String strategy,
        List<StockDayData> stockDayDataList, AddStrategyTradeStockDataRequest request) {
        if(CollectionUtils.isEmpty(stockDayDataList)){
            return null;
        }
        MovingAverage.calculateEMA(stockDayDataList, ALL_PERIODS);
        Macd.calculateMACD(stockDayDataList);
        List<String> dateList = new ArrayList<>();
        List<StrategyTradeDTO> strategyTradeDTOList = new ArrayList<>();
        for (int i = 0; i < stockDayDataList.size(); i++) {
            if (i < 800) {
                continue;
            }
            StockDayData data = stockDayDataList.get(i);
            StockDayData beforeOneData = stockDayDataList.get(i - 1);
            if (BigDecimalUtils.isLe(data.getOpenPrice(), BigDecimal.ONE)) {
                continue;
            }
            String date = data.getDate();
            if (!isLimitUp(data)) {
                continue;
            }
            if (isOneLineStock(data)) {
                continue;
            }
            //不是前俩板涨停，跳过
            int beforeLimitUpCount = getBeforeLimitUpCount(data, stockDayDataList, 3);
            if (beforeLimitUpCount == 0 || beforeLimitUpCount > 2) {
                continue;
            }
            //股价与40天内最低价相差200%以内
            StockDayData beforeFourtyLowData = StockUtils.getMinPriceDataBeforeDate(stockDayDataList, date, 40);
            BigDecimal beforeFourtyLowPrice = beforeFourtyLowData.getMinPrice();
            BigDecimal beforeFourtyLowRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                beforeFourtyLowPrice);
            if (BigDecimalUtils.isGe(beforeFourtyLowRate, twoHundred)) {
                continue;
            }
            //跳过亏损形态
            if (TulongdaoUtils.isLossImageProfitType(code, stockDayDataList, data)) {
                log.info("{}-{}-stockImageProfitType is loss", code, date);
                continue;
            }
            int afterDay = 3;
            List<StockDayData> afterDataList = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date,
                afterDay);
            if (CollectionUtils.isEmpty(afterDataList) || afterDataList.size() < 2) {
                //log.debug("{}-{}-未找到未来2天的数据，跳过", code, date);
                continue;
            }
            //StockDayData afterOneData = afterDataList.get(0);
            //StockDayData afterTwoData = afterDataList.get(1);
            //StockDayData afterThreeData = afterDataList.get(2);
            //获取涨停后的买入点
            StockDayData afterLowData = null;
            if (beforeLimitUpCount > 1) {
                //afterLowData = getTwoLimitUpAfterLowData(afterDataList, beforeOneData);
                continue;
            }

            afterLowData = TulongdaoUtils.getSignalData(code, afterDataList, data, BooleanUtils.FALSE);
            if (Objects.isNull(afterLowData)) {
                if (BooleanUtils.isFalse(request.getEarlyWarnFlag())) {
                    continue;
                }
                //预警
                afterLowData = TulongdaoUtils.getSignalData(code, afterDataList, data, BooleanUtils.TRUE);
                if (Objects.isNull(afterLowData)) {
                    continue;
                }
            }
            ////买入日跌小于5个点
            //if (BigDecimalUtils.isLe(afterLowData.getRate(), fuFive)) {
            //    continue;
            //}
            //买入点价格比涨停日高3个点，跳过
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(
                afterLowData.getTldMatchInfo().getMatchItem().getBuyPrice(),
                data.getClosePrice());
            if (BigDecimalUtils.isGt(rate, three)) {
                continue;
            }

            //20天内最低价
            StockDayData beforeTwentyLowData = StockUtils.getMinPriceDataBeforeDate(stockDayDataList, date, 20);
            BigDecimal beforeTwentyLowPrice = beforeTwentyLowData.getMinPrice();
            BigDecimal beforeTwentyLowRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                beforeTwentyLowPrice);
            //10天内最低价
            StockDayData beforeTenLowData = StockUtils.getMinPriceDataBeforeDate(stockDayDataList, date, 10);
            BigDecimal beforeTenLowPrice = beforeTenLowData.getMinPrice();
            BigDecimal beforeTenLowRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                beforeTenLowPrice);
            //10天内最高价
            StockDayData beforeTenHighData = StockUtils.getMaxPriceDataBeforeDate(stockDayDataList, date, 10);
            BigDecimal beforeTenHighPrice = beforeTenHighData.getMaxPrice();
            BigDecimal beforeTenHighRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                beforeTenHighPrice);
            String afterLowDate = afterLowData.getDate();
            BigDecimal avgApproximateRate = null;
            BigDecimal buyMinRate = null;
            if (!dateList.contains(afterLowDate)) {
                dateList.add(afterLowDate);
                StrategyTradeDTO strategyTradeDTO = new StrategyTradeDTO();
                strategyTradeDTO.setCode(code);
                strategyTradeDTO.setUuid(STATEGY_CODE);
                strategyTradeDTO.setBuyDate(DateUtil.parseDate(afterLowDate));
                strategyTradeDTO.setBuyData(afterLowData);
                //strategyTradeDTO.setStockImageProfitType(stockImageProfitType);
                //strategyTradeDTO.setStockImageCode(stockImageValue + "");
                //买入价为匹配均线上方1个点
                BigDecimal buyPrice = afterLowData.getMinPrice();
                if (beforeLimitUpCount == 1) {
                    //int matchPeriod = TulongdaoUtils.getMatchItem(afterLowData.getTldMatchInfo()).getPeriod();
                    //BigDecimal avg = getAveragePrice(afterLowData, matchPeriod);
                    //buyPrice = BigDecimalUtils.multiply(avg, new BigDecimal(1.01));
                    TldMatchItem tldMatchItem = TulongdaoUtils.getMatchItem(afterLowData.getTldMatchInfo());
                    BigDecimal avg = tldMatchItem.getAvg();
                    buyPrice = tldMatchItem.getBuyPrice();
                    avgApproximateRate = BigDecimalUtils.subStractAndDividePrecent(avg, afterLowData.getMinPrice());
                    buyMinRate = BigDecimalUtils.subStractAndDividePrecent(buyPrice, data.getMinPrice());
                }
                strategyTradeDTO.setBuyPrice(buyPrice);
                strategyTradeDTO.setBuyMinPrice(afterLowData.getMinPrice());
                strategyTradeDTO.setStatus("buy");
                BigDecimal limitSellPrice = beforeLimitUpCount == 1 ? data.getMinPrice() : beforeOneData.getMinPrice();
                if (BigDecimalUtils.isLe(afterLowData.getMinPrice(), limitSellPrice)) {
                    limitSellPrice = BigDecimalUtils.multiply(afterLowData.getMinPrice(), new BigDecimal(0.97));
                }
                strategyTradeDTO.setLimitSellPrice(limitSellPrice);
                strategyTradeDTO.setSignalPrice(
                    beforeLimitUpCount == 1 ? data.getMinPrice() : beforeOneData.getMinPrice());
                strategyTradeDTO.setBuySignalRate(
                    BigDecimalUtils.subStractAndDividePrecent(strategyTradeDTO.getSignalPrice(),
                        strategyTradeDTO.getBuyPrice()));
                strategyTradeDTO.setBuySignalMinRate(
                    BigDecimalUtils.subStractAndDividePrecent(strategyTradeDTO.getSignalPrice(),
                        strategyTradeDTO.getBuyMinPrice()));
                strategyTradeDTO.setSummary(afterLowData.getRemark());
                strategyTradeDTO.setV1(data.getClosePrice());
                strategyTradeDTO.setV2(new BigDecimal(data.getTradeNum()));
                strategyTradeDTO.setV3(beforeOneData.getClosePrice());
                strategyTradeDTO.setV4(new BigDecimal(beforeOneData.getTradeNum()));
                strategyTradeDTO.setV5(afterLowData.getClosePrice());
                StockDayData afterOneData = StockUtils.getTomorrowStockDayData(stockDayDataList, afterLowDate);
                if (Objects.nonNull(afterOneData)) {
                    strategyTradeDTO.setV6(afterOneData.getOpenPrice());
                    strategyTradeDTO.setV7(BigDecimalUtils.subStractAndDividePrecent(afterOneData.getOpenPrice(),
                        afterLowData.getClosePrice()));
                    strategyTradeDTO.setV8(BigDecimalUtils.subStractAndDividePrecent(afterLowData.getClosePrice(),
                        strategyTradeDTO.getBuyPrice()));
                    strategyTradeDTO.setV9(BigDecimalUtils.subStractAndDividePrecent(afterLowData.getClosePrice(),
                        strategyTradeDTO.getBuyPrice()));
                }
                //strategyTradeDTO.setV8(
                //    BigDecimalUtils.subStractAndDividePrecent(afterOneData.getMaxPrice(), afterOneData.getMinPrice
                //    ()));
                //strategyTradeDTO.setV9(afterTwoData.getClosePrice());
                //strategyTradeDTO.setV10(new BigDecimal(afterTwoData.getTradeNum()));
                //strategyTradeDTO.setV11(afterTwoData.getMaxPrice());
                //strategyTradeDTO.setV12(
                //    BigDecimalUtils.subStractAndDividePrecent(afterTwoData.getMaxPrice(), afterTwoData.getMinPrice
                //    ()));
                //strategyTradeDTO.setV13(afterThreeData.getClosePrice());
                //strategyTradeDTO.setV14(new BigDecimal(afterThreeData.getTradeNum()));
                //strategyTradeDTO.setV15(afterThreeData.getMaxPrice());
                //strategyTradeDTO.setV16(
                //    BigDecimalUtils.subStractAndDividePrecent(afterThreeData.getMaxPrice(),
                //        afterThreeData.getMinPrice()));
                //strategyTradeDTO.setV19(new BigDecimal(afterLowData.getMatchIndex()));
                //strategyTradeDTO.setV20(new BigDecimal(afterLowData.getMatchPeriod()));
                strategyTradeDTO.setV21(beforeFourtyLowRate);
                strategyTradeDTO.setV22(beforeTwentyLowRate);
                strategyTradeDTO.setV23(beforeTenLowRate);
                strategyTradeDTO.setV24(beforeTenHighRate);
                //均线和最低价的对比值
                strategyTradeDTO.setV27(avgApproximateRate);
                //买入价距离涨停板最低价的比值
                strategyTradeDTO.setV28(buyMinRate);
                strategyTradeDTO.setV29(afterLowData.getRate());
                BigDecimal lowPrice = BigDecimalUtils.isGt(data.getRate(), zero) ? data.getOpenPrice()
                    : data.getClosePrice();
                BigDecimal lowRate = BigDecimalUtils.subStractAndDividePrecent(lowPrice, afterLowData.getMinPrice());
                strategyTradeDTO.setV30(lowRate);
                //涨停价与60日均线的差距
                strategyTradeDTO.setV31(BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(),
                    getAveragePrice(data, sixtyPeriod)));
                strategyTradeDTO.setV32(BigDecimalUtils.multiply(data.getClosePrice(), lowRate));
                strategyTradeDTO.setV33(getAveragePrice(afterLowData, twentyPeriod));
                MacdData macd = afterLowData.getMacd();
                strategyTradeDTO.setV34(macd.getDif());
                strategyTradeDTO.setV35(macd.getDea());
                strategyTradeDTO.setV36(macd.getBar());
                BigDecimal afterMaxPrice = StockUtils.getMaxPriceAfterDate(stockDayDataList, date, 10);
                strategyTradeDTO.setV37(
                    BigDecimalUtils.subStractAndDividePrecent(afterMaxPrice, afterLowData.getClosePrice()));
                BigDecimal afterMinPrice = StockUtils.getMinPriceAfterDate(stockDayDataList, date, 10);
                strategyTradeDTO.setV38(
                    BigDecimalUtils.subStractAndDividePrecent(afterLowData.getClosePrice(), afterMinPrice));
                //strategyTradeDTO.setV39(new BigDecimal(stockImageValue));
                strategyTradeDTOList.add(strategyTradeDTO);
            }
        }
        return strategyTradeDTOList;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void doBackValidate(String code, String strategy, List<StockDayData> stockDayDataList,
        List<StrategyTradeDTO> strategyTradeDTOList) {
        List<String> dateList = strategyTradeDTOList.stream().map(o -> DateUtil.formatDate(o.getBuyDate())).collect(
            Collectors.toList());
        Date latestEndDate = DateUtil.parseDate(dateList.get(0));
        latestEndDate = DateUtil.offsetDay(latestEndDate, -1);
        for (StrategyTradeDTO strategyTradeDTO : strategyTradeDTOList) {
            Date d = strategyTradeDTO.getBuyDate();
            if (DateUtils.beforeOrEquals(d, latestEndDate)) {
                log.debug("{}-{}在上一个周期内,忽略", code, d);
                continue;
            }
            getTradeInfo(code, stockDayDataList, strategyTradeDTO);
            latestEndDate = strategyTradeDTO.getSellDate();
        }
    }

    private void getTradeInfo(String code, List<StockDayData> stockDayDataList, StrategyTradeDTO strategyTradeDTO) {
        String date = DateUtil.formatDate(strategyTradeDTO.getBuyDate());
        //log.debug("{}-{}>>>>>股票信息:{}", code, date, strategyTradeDTO.getSummary());
        StockDayData buyDayData = StockUtils.getStockDayData(stockDayDataList, date);
        BigDecimal buyPrice = buyDayData.getMinPrice();
        List<StockDayData> subList = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date, 100);
        if (CollectionUtils.isEmpty(subList) || subList.size() < 2) {
            return;
        }
        int size = subList.size();
        StockDayData firtData = subList.get(0);
        String firstDate = firtData.getDate();
        BigDecimal closeRate = null;
        String sellDate = null;
        String info = "";
        BigDecimal sellPrice = null;
        BigDecimal rate = closeRate;
        int holdIndex = 0;
        BigDecimal rangeMaxPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeMaxRate = BigDecimal.ZERO;
        int rangeMaxIndex = 0;
        BigDecimal rangeCloseMaxPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeCloseMaxRate = BigDecimal.ZERO;
        BigDecimal rangeMinPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeMinRate = BigDecimal.ZERO;
        int rangeMinIndex = 0;
        for (int i = 0; i < 10; i++) {
            if (i == size) {
                return;
            }
            StockDayData data = subList.get(i);
            //计算长上影线
            BigDecimal upPrice = BigDecimalUtils.isGt(data.getRate(), zero) ? data.getClosePrice()
                : data.getOpenPrice();
            BigDecimal highRate = BigDecimalUtils.subStractAndDividePrecent(data.getMaxPrice(), upPrice);
            if (i == 0) {
                strategyTradeDTO.setV17(BigDecimalUtils.subStractAndDividePrecent(data.getOpenPrice(),
                    strategyTradeDTO.getBuyData().getClosePrice()));
                strategyTradeDTO.setV18(highRate);
            }
            StockDayData yesterdayDayData = i == 0 ? data : subList.get(i - 1);
            BigDecimal closePrice = data.getClosePrice();
            closeRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, buyPrice);
            sellDate = data.getDate();
            sellPrice = data.getClosePrice();
            holdIndex = i;
            BigDecimal upRate = BigDecimalUtils.subStractAndDividePrecent(data.getMaxPrice(), buyPrice);
            BigDecimal openRange = data.getOpenPrice().subtract(buyPrice);
            BigDecimal openRate = BigDecimalUtils.divideToPrecent(openRange, buyPrice);
            if (BigDecimalUtils.isGt(data.getMaxPrice(), rangeMaxPrice)) {
                rangeMaxPrice = data.getMaxPrice();
                rangeMaxRate = BigDecimalUtils.subStractAndDividePrecent(rangeMaxPrice, buyPrice);
                rangeMaxIndex = i;
            }
            if (BigDecimalUtils.isLt(data.getMinPrice(), rangeMinPrice)) {
                rangeMinPrice = data.getMinPrice();
                rangeMinRate = BigDecimalUtils.subStractAndDividePrecent(rangeMinPrice, buyPrice);
                rangeMinIndex = i;
            }
            rangeCloseMaxPrice = BigDecimalUtils.isGt(data.getClosePrice(), rangeCloseMaxPrice) ? data.getClosePrice()
                : rangeCloseMaxPrice;
            rangeCloseMaxRate = BigDecimalUtils.subStractAndDividePrecent(rangeCloseMaxPrice, buyPrice);

            BigDecimal realtimeRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(), buyPrice);
            rate = closeRate;
            String sellInfo = "收盘价卖出";
            if (isLimitUp(data)) {
                info += "第" + (i + 1) + "天涨停,拿住;";
                continue;
            }
            ////有长上影线,就在上影线一半的位置卖掉
            //if (BigDecimalUtils.isGe(highRate, four)) {
            //    BigDecimal price = BigDecimalUtils.add(data.getMaxPrice(), upPrice);
            //    sellPrice = BigDecimalUtils.divide(price, two);
            //    BigDecimalUtils.subStractAndDividePrecent(sellPrice, buyPrice);
            //    sellInfo = "冲高卖出";
            //    log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo,
            //    rate,
            //        openRate, closeRate, upRate);
            //    info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
            //        + ",最高点收益=" + upRate;
            //    break;
            //}
            // 跌破止损价，跑路
            if (BigDecimalUtils.isLt(data.getClosePrice(), strategyTradeDTO.getLimitSellPrice())) {
                sellInfo = "跌破止损价,止损";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            //收盘价卖出
            log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                openRate, closeRate, upRate);
            sellInfo = "收盘价正常卖出";
            info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                + ",最高点收益=" + upRate;
            break;
        }
        strategyTradeDTO.setHoldDay(holdIndex + 1);
        strategyTradeDTO.setSellDate(DateUtil.parseDate(sellDate));
        strategyTradeDTO.setSellPrice(sellPrice);
        strategyTradeDTO.setProfitRate(rate);
        strategyTradeDTO.setRangeMaxPrice(rangeMaxPrice);
        strategyTradeDTO.setRangeMaxRate(rangeMaxRate);
        strategyTradeDTO.setRangeMinPrice(rangeMinPrice);
        strategyTradeDTO.setRangeMinRate(rangeMinRate);
        String downBuyPriceInfo = BigDecimalUtils.isLt(rangeMinPrice, strategyTradeDTO.getBuyMinPrice()) ? "有" : "未";
        StockDayData maxData = subList.get(rangeMaxIndex);
        BigDecimal highRate = BigDecimalUtils.subStractAndDividePrecent(maxData.getMaxPrice(), maxData.getClosePrice());
        String rangeInfo = "第" + (rangeMinIndex + 1) + "天为最低价," + downBuyPriceInfo + "跌破买入价,第" + (rangeMaxIndex + 1)
            + "天为最高价,与收盘价差为" + highRate + "%;";
        strategyTradeDTO.setFollowInfo(rangeInfo);
        strategyTradeDTO.setStrategyDescription(info);
        strategyTradeDTO.setStatus("sell");
        strategyTradeDTO.setWinFlag(BigDecimalUtils.isGt(rate, BigDecimal.ZERO) ? "y" : "n");
        //是否跌破止损价
        //strategyTradeDTO.setV19(BigDecimalUtils.isLt(rangeMinPrice, strategyTradeDTO.getLimitSellPrice()) ? one :
        // zero);
        //strategyTradeDTO.setV20(highRate);
    }

}
